package com.nulldev.util.web.HttpServer3;

import static com.nulldev.util.manage.Macros.__assert;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.VariableAPI.util.strings.splitters.Splitters;
import com.nulldev.util.data.Variables;
import com.nulldev.util.io.FastIO.FastIO;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;

public class ByteReadRangeParser {

	private static final Logger log = LoggerFactory.getLogger();
	private static final long BRR_IGNORED = -1L;

	public static ByteReadRange[] parse(final String rangeHeader) {
		Variables.requireNonNull(rangeHeader, "rangeHeader");
		if (StringsUtil.isBlank(rangeHeader))
			return new ByteReadRange[0];

		final String[] amble = Splitters.splitString(rangeHeader, "=");

		__assert(amble.length != 2, "amble.length != 2 (got " + amble.length + ")");

		final String unit = amble[0];

		if (!unit.equals("bytes")) {
			log.warn("Skipping range due to unknown unit: " + unit);
			return new ByteReadRange[0];
		}

		final String[] segments = Splitters.splitString(amble[1], ", ");
		final ByteReadRange[] ranges = new ByteReadRange[segments.length];

		for (int i = 0; i < segments.length; i++) {
			final String segment = segments[i];

			final String[] segmentRanges = StringsUtil.trimAllWhitespace(segment).split("-");

			long min = BRR_IGNORED, max = BRR_IGNORED;
			if (segmentRanges.length == 2) {
				min = MathUtil.safeParseL(segmentRanges[0], BRR_IGNORED);
				max = MathUtil.safeParseL(segmentRanges[1], BRR_IGNORED);
			} else {
				if (segment.endsWith("-")) {
					min = MathUtil.safeParseL(segmentRanges[0], BRR_IGNORED);
				} else {
					min = MathUtil.safeParseL(segment, BRR_IGNORED);
				}
			}

			final ByteReadRange brr = new ByteReadRange(min, max);
			ranges[i] = brr;
		}

		return ranges;
	}

	private static ByteReadRange _assert(final String val, final long min, final long max) {
		final ByteReadRange[] out = parse(val);
		__assert(out.length != 1, "out.length != 1");
		final ByteReadRange brr = out[0];
		__assert(brr.min != min, "brr.min (" + brr.min + ") != min (" + min + ")");
		__assert(brr.max != max, "brr.max (" + brr.max + ") != max (" + max + ")");
		return brr;
	}

	private static void _assert(final String val, final long[][] min_max) {
		final ByteReadRange[] out = parse(val);
		__assert(out.length != min_max.length, "out.length != min_max.length");
		for (int i = 0; i < out.length; i++) {
			final ByteReadRange brr = out[i];
			final long[] min_max_instance = min_max[i];
			__assert(min_max_instance.length != 2, "min_max_instance.length != 2");
			final long min = min_max_instance[0];
			final long max = min_max_instance[1];
			__assert(brr.min != min, "brr.min != min");
			__assert(brr.max != max, "brr.max (" + brr.max + ") != max (" + max + ")");
		}
	}

	public static void main(String[] args) {
		{
			_assert("bytes=0-", 0, -1);
		}
		{
			_assert("bytes=200-999, 2000-2499, 9500-", new long[][]
				{
						{ 200, 999 },
						{ 2000, 2499 },
						{ 9500, -1 } });
		}
		{
			// FIXME: I imply that there is 102 bytes to be read! That is incorrect.
			final ByteReadRange _temp = _assert("bytes=-100", -1, 100);
			__assert(_temp.length() != 100, "len (" + _temp.length() + ") != 100");
		}
		{
			final ByteReadRange _temp = _assert("bytes=0-499", 0, 499);
			__assert(_temp.length() != 500, "len (" + _temp.length() + ") != 500");
		}
		{
			_assert("bytes=900-", 900, -1);
		}
		{
			_assert("bytes=3340509-", 3340509, -1);
		}
		JVM.println("All tests passed, exiting...");
		FastIO.get().flush();
		JVM.exit();
	}
}
